package com.example.chatrecommendation.tool;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.chatrecommendation.bean.dto.Messages;
import com.example.chatrecommendation.bean.entity.Agent;
import com.example.chatrecommendation.bean.entity.Resource;
import com.example.chatrecommendation.bean.vo.PPTLink;
import com.example.chatrecommendation.bean.vo.VideoLink;
import com.example.chatrecommendation.mapper.ResourceMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Relationship;
import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
@Component
public class Api {

    private String URL_STR = "http://localhost:11434/api/chat";
    private final Neo4jCypherExecutor neo4jCypherExecutor;
    private final ResourceMapper resourceMapper;

    public Api(Neo4jCypherExecutor neo4jCypherExecutor, ResourceMapper resourceMapper) {
        this.neo4jCypherExecutor = neo4jCypherExecutor;
        this.resourceMapper = resourceMapper;
    }

    public List<Map<String, Object>> getKnowledgeGraph(List<Messages> message) throws MalformedURLException {
        String prompt = "请根据以下信息，识别出其中的实体，并返回实体列表，如果无法识别，超出了知识范围，必须返回空字符串，例如：\\n用户提问：Java中的集合框架有哪些常用的接口和类？\\n对应的返回是：\\nJava,集合,框架,常用,接口,类";
        StringBuilder response = getOllamaResponse(prompt, message, true);

//		图数据库查询语句
        String content = response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"},\"done_reason\""));
        content = content.replace("\\n", " ");
        System.out.println("content = " + content);
        String[] split = content.split(",");

//      执行图数据库查询语句
        List<Map<String, Object>> maps;
        try {
            //		2. 根据识别出来的实体，查找知识图谱节点，并返回节点信息
            maps = executeQueryWithLabels(List.of(split));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        for (Map<String, Object> row : maps) {
            for (Map.Entry<String, Object> entry : row.entrySet()) {
                String columnName = entry.getKey();
                Object columnValue = entry.getValue();
                System.out.println(columnName + ": " + columnValue);
                log.info("columnName = " + columnName + " columnValue = " + columnValue);
            }
        }
//		如果maps的元素数量超过了100个，则截取前99个
        if (maps.size() >= 60) {
            maps = maps.subList(0, 60);
        }
        return maps;
    }

    @Nullable
    private StringBuilder getOllamaResponse(String prompt, List<Messages> message, Boolean isSearch) throws MalformedURLException {
        // 创建http连接
        URL url = new URL(URL_STR);
        HttpURLConnection connection;
        try {
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        String jsonInputString = null;
        connection.setDoOutput(true);
        // 调用大模型
        if (isSearch) {
            message.get(0).setContent(prompt);
            jsonInputString = String.format("""
                    {
                        "model": "qwen2.5:7b",
                        "messages": """ + List.of(message.get(0), message.get(message.size() - 1)) + """
                        ,"stream": false
                    }""");//   只取消息记录的第一条和最后一条，为了减少模型提取关键字的复杂度
        } else {
            jsonInputString = String.format("""
                    {
                        "model": "qwen2.5:7b",
                        "messages":""" + message + """
                        ,"stream": false
                    }""");
        }

        try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
            wr.write(jsonInputString.getBytes(StandardCharsets.UTF_8));
            wr.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        StringBuilder response = new StringBuilder();
        try (BufferedReader in = new BufferedReader(
                new InputStreamReader(connection.getInputStream()))) {
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        connection.disconnect();
        return response;
    }

    private List<Map<String, Object>> executeQueryWithLabels(List<String> labels) {
        // 动态构建Cypher查询
        StringBuilder queryBuilder = new StringBuilder();
        queryBuilder.append("MATCH (n) WHERE ");

        for (int i = 0; i < labels.size(); i++) {
            String label = labels.get(i);
            queryBuilder.append("ANY(l IN labels(n) WHERE l CONTAINS '").append(label).append("')");
            if (i < labels.size() - 1) {
                queryBuilder.append(" OR ");
            }
        }

        queryBuilder.append(" OPTIONAL MATCH (n)-[r]->(related) RETURN n, r, related");

        String query = queryBuilder.toString();

        List<Map<String, Object>> res = neo4jCypherExecutor.executeCypherQuery(query);
        return transformResult(res);
    }

    private List<Map<String, Object>> transformResult(List<Map<String, Object>> rawResult) {
        return rawResult.stream().map(this::transformRow).toList();
    }

    private Map<String, Object> transformRow(Map<String, Object> row) {
        Map<String, Object> transformedRow = new HashMap<>();

        row.forEach((key, value) -> {
            if (value instanceof Node) {
                transformedRow.put(key, transformNode((Node) value));
            } else if (value instanceof Relationship) {
                transformedRow.put(key, transformRelationship((Relationship) value));
            } else {
                transformedRow.put(key, value);
            }
        });

        return transformedRow;
    }

    private Map<String, Object> transformNode(Node node) {
        Map<String, Object> nodeMap = new HashMap<>();
        nodeMap.put("id", node.id());
        nodeMap.put("labels", node.labels());
        nodeMap.put("properties", node.asMap());
        return nodeMap;
    }

    private Map<String, Object> transformRelationship(Relationship relationship) {
        Map<String, Object> relationshipMap = new HashMap<>();
        relationshipMap.put("id", relationship.id());
        relationshipMap.put("startNodeId", relationship.startNodeId());
        relationshipMap.put("endNodeId", relationship.endNodeId());
        relationshipMap.put("type", relationship.type());
        relationshipMap.put("properties", relationship.asMap());
        return relationshipMap;
    }

    public Pair<List<PPTLink>, List<VideoLink>> getPPTAndVideo(List<Messages> message) throws MalformedURLException {
        String prompt = "请根据用户提出的内容，分析其中最可能以及最重要的java基础知识点名称，例如：\\\\n用户提问：Java中的集合框架有哪些常用的接口和类？\\\\n对应的返回是：\\\\n数组,类与对象,常用JavaAPI之集合类,接口,常用\\\\n禁止事项：不能出现类似于'列表(List)'中文后面括号英文的情况";
        StringBuilder response = getOllamaResponse(prompt, message, true);
        // 解析字符串
        String content = response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"},\"done_reason\""));
        content = content.replace("\\n", " ");
        String[] split = content.split(",");
        System.out.println("split = " + Arrays.toString(split));
        LambdaQueryWrapper<Resource> wrapper = new LambdaQueryWrapper<>();
// 查询type为1的资源
        wrapper.eq(Resource::getType, 1);
// 添加模糊匹配的条件
        wrapper.and((LambdaQueryWrapper<Resource> w) -> {
            for (int i = 0; i < split.length; i++) {
                if (i > 0) {
                    w.or();
                }
                w.like(Resource::getName, split[i]);
            }
        });
        List<Resource> resources = resourceMapper.selectList(wrapper);
        List<PPTLink> pptLinks = new ArrayList<>();
        if (resources.size() > 0) {
            for (Resource resource : resources) {
                PPTLink pptLink = new PPTLink();
                pptLink.setName(resource.getName());
                pptLink.setSrc(resource.getCover());
                pptLink.setPptSrc(resource.getValue());
                pptLinks.add(pptLink);
            }
//			如果pptlinks元素大于2则调整为2
            if (pptLinks.size() > 2) {
//				取前两个元素
                pptLinks = pptLinks.subList(0, 2);
            }
        }

        LambdaQueryWrapper<Resource> wrapper1 = new LambdaQueryWrapper<>();
//		查询视频
        wrapper1.eq(Resource::getType, 0);
        wrapper1.and(i -> i.notLike(Resource::getName, "%异常%")); // 排除包含“异常”关键字的资源

        // 添加模糊匹配的条件
        wrapper1.and((LambdaQueryWrapper<Resource> w) -> {
            for (int i = 0; i < split.length; i++) {
                if (i > 0) {
                    w.or();
                }
                w.like(Resource::getName, split[i]);
            }
        });
        List<Resource> resources1 = resourceMapper.selectList(wrapper1);
        List<VideoLink> videoLinks = new ArrayList<>();
        if (resources1.size() > 0) {
            for (Resource resource : resources1) {
                VideoLink videoLink = new VideoLink();
                videoLink.setName(resource.getName());
                videoLink.setSrc(resource.getCover());
                videoLink.setVideoSrc(resource.getValue());
                videoLinks.add(videoLink);
            }
//			如果元素大于2，则调整为2
            if (videoLinks.size() > 2) {
//				取前两位元素
                videoLinks = videoLinks.subList(0, 2);
            }
        }
        return Pair.of(pptLinks, videoLinks);
    }

    public Agent getAgent(String description) throws MalformedURLException {
        List<Messages> message = new ArrayList<>();
        message.add(new Messages("system", "现在你是一个创建智能体配置的助手，请根据用户的输入期望，生成智能体的name、description、config以及三个**以用户的角度用于测试智能体**的问题recommend1、recommend2、recommend3，记住只能生成要求的这些项目。要求为json格式，尽量全是中文。\\n用户输入："));
        message.add(new Messages("user", description));
        StringBuilder response = getOllamaResponse(null, message, false);

        //      解析字符串
        String content = response.substring(response.indexOf("```json") + 9, response.indexOf("\\n```"));
        content = content.replace("\\n", " ")
                .replace("\\", " ")
                .replace(" ", "");
        System.out.println("content = " + content);
        String name;
        String description1;
        JSONObject config;
        String recommend1;
        String recommend2;
        String recommend3;
        String configStr;
        try {
            JSONObject jsonObj = new JSONObject(content);

            // 提取名称
            name = jsonObj.getString("name");
            System.out.println("名称: " + name);
            // 提取描述
            description1 = jsonObj.getString("description");
            System.out.println("描述: " + description1);

            // 提取配置信息
            config = jsonObj.getJSONObject("config");
            System.out.println("配置信息: " + config);
//		    替换配置中json格式的双引号，替换为单引号，为接下来的传递给大模型
            configStr = config.toString().replace("\"", "'");
            // 提取推荐问题
            recommend1 = jsonObj.getString("recommend1");
            recommend2 = jsonObj.getString("recommend2");
            recommend3 = jsonObj.getString("recommend3");
            System.out.println("推荐问题1: " + recommend1);
            System.out.println("推荐问题2: " + recommend2);
            System.out.println("推荐问题3: " + recommend3);
        } catch (JSONException e) {
            e.printStackTrace();
            throw new RuntimeException("JSON解析错误，模型生成json数据有概率出错，属于正常现象，请重试");
        }
        Agent agent = new Agent();
        agent.setName(name);
        agent.setDescription(description1);
        agent.setConfig(configStr);
        agent.setRecommend1(recommend1);
        agent.setRecommend2(recommend2);
        agent.setRecommend3(recommend3);
        return agent;
    }

    public String callLargeModel(List<Messages> message) throws MalformedURLException {
        StringBuilder response = getOllamaResponse(null, message, false);
        //		解析大模型的回答
        return response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"},\"done_reason\""));

    }

    public String optimizeQuestion(String question) throws MalformedURLException {
        List<Messages> message = new ArrayList<>();
        message.add(new Messages("system", "你的角色是优化以下提示词，要求用一句话优化语句，不要直接回答用户的问题，也不要向用户发问，而是帮助用户完善他的问题。\\n用户输入："));
        message.add(new Messages("user", question));
        StringBuilder response = getOllamaResponse(null, message, false);
        if (response != null) {
            return response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"},\"done_reason\""));
        }
        return "大模型错误！回复内容为NULL！";
    }

    public String getChatRecordsName(List<Messages> message) throws MalformedURLException {
        String prompt = "请根据用户的提问生成一个聊天记录名称，字数在十个字以内，不要回答用户的问题，仅需总结对话内容，给本轮对话起名。";
        message.get(0).setContent(prompt);
        StringBuilder response = getOllamaResponse(prompt, message, false);
        return response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"},\"done_reason\""));
    }

    public String translate(String query) throws MalformedURLException {
        List<Messages> message = new ArrayList<>();
        String content = """
# Stable Diffusion prompt 助理你来充当一位有艺术气息的Stable Diffusion prompt 助理。## 任务我用自然语言告诉你要生成的prompt的主题，你的任务是根据这个主题想象一幅完整的画面，然后转化成一份详细的、高质量的prompt，让Stable Diffusion可以生成高质量的图像。## 背景介绍Stable Diffusion是一款利用深度学习的文生图模型，支持通过使用 prompt 来产生新的图像，描述要包含或省略的元素。## prompt 概念- prompt 用来描述图像，由普通常见的单词构成，使用英文半角“,”做为分隔符。- 以“,”分隔的每个单词或词组称为 tag。所以prompt是由系列由“,”分隔的tag组成的。## () 和 [] 语法调整关键字强度的等效方法是使用 () 和 []。 (keyword) 将tag的强度增加 1.1 倍，与 (keyword:1.1) 相同，最多可加三层。 [keyword] 将强度降低 0.9 倍，与 (keyword:0.9) 相同。## Prompt 格式要求下面我将说明 prompt 的生成步骤，这里的 prompt 可用于描述人物、风景、物体或抽象数字艺术图画。你可以根据需要添加合理的、但不少于5处的画面细节。### 1. prompt 要求- prompt 内容包含画面主体、材质、附加细节、图像质量、艺术风格、色彩色调、灯光等部分，但你输出的 prompt 不能分段，例如类似“medium:”这样的分段描述是不需要的，也不能包含“:”和“.”。- 画面主体：不简短的英文描述画面主体, 如 A girl in a garden，主体细节概括（主体可以是人、事、物、景）画面核心内容。这部分根据我每次给你的主题来生成。你可以添加更多主题相关的合理的细节。- 对于人物主题，你必须描述人物的眼睛、鼻子、嘴唇，例如'beautiful detailed eyes,beautiful detailed lips,extremely detailed eyes and face,longeyelashes'，以免Stable Diffusion随机生成变形的面部五官，这点非常重要。你还可以描述人物的外表、情绪、衣服、姿势、视角、动作、背景等。人物属性中，1girl表示一个女孩，2girls表示两个女孩。- 材质：用来制作艺术品的材料。 例如：插图、油画、3D 渲染和摄影。 Medium 有很强的效果，因为一个关键字就可以极大地改变风格。- 附加细节：画面场景细节，或人物细节，描述画面细节内容，让图像看起来更充实和合理。这部分是可选的，要注意画面的整体和谐，不能与主题冲突。- 艺术风格：这部分描述图像的风格。加入恰当的艺术风格，能提升生成的图像效果。常用的艺术风格例如：portraits,landscape,horror,anime,sci-fi,photography,concept artists等。- 色彩色调：颜色，通过添加颜色来控制画面的整体颜色。- 灯光：整体画面的光线效果。### 2. 限制：- tag 内容用英语单词或短语来描述，并不局限于我给你的单词。注意只能包含关键词或词组。- 注意不要输出句子，不要有任何解释。- tag数量限制40个以内，单词数量限制在60个以内。- tag不要带引号(“\\\"”)。- 使用英文半角“,”做分隔符。- tag 按重要性从高到低的顺序排列。用户的第一个主题是：""";
        message.add(new Messages("system", content));
        message.add(new Messages("user", query));
        StringBuilder response = getOllamaResponse(null, message, false);
        if (response != null) {
            return response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"},\"done_reason\""));
        }
        return query;
    }
}
